package com.gl.glcourses.services.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.velocity.tools.generic.DateTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.flex.remoting.RemotingDestination;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.gl.glcourses.calendar.ICalendarManager;
import com.gl.glcourses.mail.impl.SendEdicionCurso;
import com.gl.glcourses.mail.impl.SendSolicitudParaRealizarCurso;
import com.gl.glcourses.repository.ICalendarRepository;
import com.gl.glcourses.repository.IEdicionCursoRepository;
import com.gl.glcourses.repository.IParticipanteRepository;
import com.gl.glcourses.repository.IStaticRepository;
import com.gl.glcourses.services.IEdicionCursoServices;
import com.gl.glcourses.services.IParticipanteServices;
import com.gl.glcourses.services.IStaticServices;
import com.gl.glcourses.services.IUsuarioServices;
import com.gl.glcourses.vos.Confirmacion;
import com.gl.glcourses.vos.EdicionCurso;
import com.gl.glcourses.vos.EstadoEdicionCurso;
import com.gl.glcourses.vos.EstadoParticipante;
import com.gl.glcourses.vos.EstadoPresentismoSesionEdicion;
import com.gl.glcourses.vos.Oficina;
import com.gl.glcourses.vos.Participante;
import com.gl.glcourses.vos.PresentismoSesionEdicionCurso;
import com.gl.glcourses.vos.SesionEdicionCurso;
import com.gl.glcourses.vos.SesionEdicionCursoCalendarEvent;
import com.gl.glcourses.vos.StaticHoraMes;
import com.gl.glcourses.vos.StaticHorasOficina;
import com.gl.glcourses.vos.TipoParticipante;
import com.gl.glcourses.vos.TipoUsuario;
import com.gl.glcourses.vos.Usuario;
import com.gl.glcourses.vos.constants.Constants;

@Service
@RemotingDestination(channels = { "my-amf" })
@Transactional
public class EdicionCursoServices implements IEdicionCursoServices{

	@Autowired
	IParticipanteServices participanteServices;
	@Autowired
	IEdicionCursoRepository repoEdicionCurso;
	@Autowired
	IParticipanteRepository repoParticipante;
	@Autowired
	ICalendarManager managerCalendar;
	@Autowired
	ICalendarRepository repoCalendar;
	@Autowired
	SendEdicionCurso sendEdicionCurso;
	@Autowired
	IUsuarioServices iUsuarioServices;
	@Autowired
	SendSolicitudParaRealizarCurso sendSolicitudParaRealizarCurso;
	@Autowired
	IStaticRepository<Object> repoStatic;
	@Autowired
	IStaticServices staticService;
	
	@Transactional
	@Override
	public Confirmacion crearEdicionCurso(EdicionCurso edicionCurso, boolean cursoAbierto)
			throws Exception {

		Confirmacion confirmacion = new Confirmacion();
		int minutosTotales = 0;
		int minutosParciales = 0;

		//valido curso
		if(edicionCurso.getCurso() == null){
			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_CURSO);
			return confirmacion;
		}
		//valido curso fecha baja
		if(edicionCurso.getCurso().getFechaBaja() != null){
			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_CURSO_BAJA);
			return confirmacion;
		}
		//valido oficina
		if(edicionCurso.getOficina() == null){
			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_OFICINA);
			return confirmacion;
		}
		
		//valido cupos
//		if(edicionCurso.getCupos() == 0){
//			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
//			confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_CUPOS);
//			return confirmacion;
//		}
		
		//valido oficna fecha baja
		for (Oficina oficina: edicionCurso.getOficina()){
			if(oficina.getFechaBaja() != null){
				confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
				confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_OFICINA_BAJA);
				return confirmacion;
			}
		}
		//valido sesion curso
		if(edicionCurso.getSesionCurso() == null){
			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_SESION);
			return confirmacion;
		}
		//valido lugar de sesion curso
		//creo fecha desde y fecha hasta
		//obtengo los minutos parciales
		Date fechaDesde = edicionCurso.getSesionCurso().get(0).getFecha();
		Date fechaHasta = edicionCurso.getSesionCurso().get(0).getFecha();
		for(SesionEdicionCurso sesion : edicionCurso.getSesionCurso()){
			minutosParciales += sesion.getCantidadMinutos();			
			if(sesion.getLugar().equals(null)){
				confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
				confirmacion.setErrorString(Constants.SESIONEDICIONCURSO_ERROR_LUGAR);
				return confirmacion;
			}
			if(fechaDesde.after(sesion.getFecha())){
				fechaDesde = sesion.getFecha();
			}
			if(fechaHasta.before(sesion.getFecha())){
				Calendar calendar = new GregorianCalendar();
				calendar.setTime(sesion.getFecha());
				calendar.add(Calendar.MINUTE, sesion.getCantidadMinutos());
				fechaHasta = calendar.getTime();
			}
		}
		//valido minuto sesiones y minutos curso
		minutosTotales = edicionCurso.getCurso().getCantidadMinutosTotal();
		if(minutosParciales != minutosTotales){
			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			confirmacion.setErrorString(Constants.SESIONEDICIONCURSO_ERROR_TIEMPOS);
			return confirmacion;
		}

		//le seteo fecha desde y hasta
		edicionCurso.setFechaDesde(fechaDesde);
		edicionCurso.setFechaHasta(fechaHasta);

		//le seteo la edicion curso a las sesiones
		for(SesionEdicionCurso sesionEdicionCurso : edicionCurso.getSesionCurso()){
			sesionEdicionCurso.setEdicionCurso(edicionCurso);
		}

		//recupero los participantes
		List<Participante> participantesTemp = edicionCurso.getParticipantes();
		edicionCurso.setParticipantes(new ArrayList<Participante>());

		//guardo edicion curso
		EdicionCurso edicionCursoBD = (EdicionCurso)repoEdicionCurso.saveEdicion(edicionCurso);

		//genero los eventos en google calendar si viene para publicar
		List<SesionEdicionCursoCalendarEvent> sesionEdicionCursoCalendarEvents = new ArrayList<SesionEdicionCursoCalendarEvent>();
		if(edicionCurso.getEstadoEdicionCurso().equals(EstadoEdicionCurso.PUBLICADO)){
			sesionEdicionCursoCalendarEvents = managerCalendar.agendarEdicion(edicionCursoBD);
			
			if(sesionEdicionCursoCalendarEvents.size() == 0){
				//TODO arreglar esto
				repoEdicionCurso.delete(edicionCursoBD);
				confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
				//TODO
				//confirmacion.setErrorString(Constants.);
				return confirmacion;
			}
		}
		if(cursoAbierto)
		{
			ArrayList<Usuario> listaUsuarios = new ArrayList<Usuario>();
			for(Oficina oficina:edicionCursoBD.getOficina()){
				for(Usuario usuario: iUsuarioServices.buscarUsuariosSimplexOficina(oficina)){
					listaUsuarios.add(usuario);
					for(Participante participanteInvitado : participantesTemp){
						if(participanteInvitado.getUsuario().getNombre().compareTo(usuario.getNombre())==0){
							listaUsuarios.remove(usuario);
						}
					}
				}
			}
			ArrayList<Participante> docentes = new ArrayList<Participante>();
			for(Participante participante:participantesTemp){
				if(participante.getTipoParticipante().toString().equalsIgnoreCase(TipoParticipante.DOCENTE.toString())){
					docentes.add(participante);
				}
			}
			
			SimpleMailMessage msg = new SimpleMailMessage();
			Map<Object, Object> hTemplateVariables = new HashMap<Object, Object>();
			hTemplateVariables.put("usuarios", listaUsuarios);
			hTemplateVariables.put("edicioncurso", edicionCursoBD);
			hTemplateVariables.put("sesionedicioncurso", new SesionEdicionCurso());
			hTemplateVariables.put("docentes", docentes);
			hTemplateVariables.put("oficina", new Oficina());
			hTemplateVariables.put("participante",new Participante());
			hTemplateVariables.put("date", new DateTool());
			hTemplateVariables.put("cuenta", 0);
			String descripcion = ""; 
			if(edicionCursoBD.getCurso().getTematica() == null || edicionCursoBD.getCurso().getTematica().trim().isEmpty()){
				descripcion="";
			}else{
				descripcion=edicionCursoBD.getCurso().getTematica();
			}
			hTemplateVariables.put("descripcion", descripcion);
			
			String objetivo = ""; 
			if(edicionCursoBD.getCurso().getObjetivo() == null || edicionCursoBD.getCurso().getObjetivo().trim().isEmpty()){
				objetivo="";
			}else{
				objetivo=edicionCursoBD.getCurso().getObjetivo();
			}
			hTemplateVariables.put("objetivo", objetivo);
			
			msg.setSubject("Invitacion a Curso");
			sendEdicionCurso.send(msg, hTemplateVariables);
		}
		
		for(SesionEdicionCursoCalendarEvent sesionEdicionCursoCalendarEvent : sesionEdicionCursoCalendarEvents){	
			repoCalendar.saveUpdate(sesionEdicionCursoCalendarEvent);
		}

		if(participantesTemp.size() > 0 ){
			edicionCursoBD.setParticipantes(participantesTemp);
			participanteServices.saveParticipantesInvitados(edicionCursoBD.getParticipantes());
			for(Participante participante : edicionCursoBD.getParticipantes()){
				managerCalendar.agregarInvitado(participante, repoCalendar.getSesionEdicionCursoCalendarEvent(edicionCursoBD));
			}
		}

		
		//Estadisticas de cursos
		List<Object>listStaticOficina = staticService.listStaticHorasOficina();
		for(SesionEdicionCurso sesionStatic : edicionCursoBD.getSesionCurso())
		{
			StaticHoraMes newStatic = new StaticHoraMes();
			newStatic.setCantMinutos(sesionStatic.getCantidadMinutos());
			newStatic.setFecha(sesionStatic.getFecha());
			newStatic = (StaticHoraMes) repoStatic.saveUpdate(newStatic);
			
			for(Oficina oficina : edicionCursoBD.getOficina()){
				for(Object staticOficina : listStaticOficina){
					if( oficina.getId() == ((StaticHorasOficina)staticOficina).getOficina().getId() ){
						((StaticHorasOficina)staticOficina).getListStaticHoraMes().add(newStatic);
					}
				}
			}
			
		}
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);

		return confirmacion;
	}

	//revisar
	@Transactional
	@Override
	public Confirmacion publicarEdicionCurso(EdicionCurso edicionCurso)
			throws Exception {

		Confirmacion confirmacion = new Confirmacion();
		
		//no gusta tanto esto pero es la unica manera de que ande :(
		EdicionCurso edicionCursoBD = (EdicionCurso)repoEdicionCurso.findById(edicionCurso.getId(), EdicionCurso.class);

		//genero los eventos en google calendar si viene para publicar
		List<SesionEdicionCursoCalendarEvent> sesionEdicionCursoCalendarEvents = new ArrayList<SesionEdicionCursoCalendarEvent>();
		sesionEdicionCursoCalendarEvents = managerCalendar.agendarEdicion(edicionCursoBD);
		//TODO mandar mails
		//si es 0 es xq algo salio mal
		if(sesionEdicionCursoCalendarEvents.size() == 0){
			//TODO arreglar esto
			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			//TODO
			//confirmacion.setErrorString(Constants.);
			return confirmacion;
		}

		for(SesionEdicionCursoCalendarEvent sesionEdicionCursoCalendarEvent : sesionEdicionCursoCalendarEvents){	
			repoCalendar.saveUpdate(sesionEdicionCursoCalendarEvent);
		}

		edicionCursoBD.setEstadoEdicionCurso(EstadoEdicionCurso.PUBLICADO);
		//guardo edicion curso
		repoEdicionCurso.saveUpdate(edicionCursoBD);

       
		//recupero los participantes
		List<Participante> participantesTemp = edicionCurso.getParticipantes();
		edicionCurso.setParticipantes(new ArrayList<Participante>());
		//en proceso, agrega los usuarios invitados al calendar
		if(participantesTemp.size() > 0 ){
			edicionCursoBD.setParticipantes(participantesTemp);
			participanteServices.saveParticipantesInvitados(edicionCursoBD.getParticipantes());
			for(Participante participante : edicionCursoBD.getParticipantes()){
				managerCalendar.agregarInvitado(participante, repoCalendar.getSesionEdicionCursoCalendarEvent(edicionCursoBD));
			}
		}

		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		
		return confirmacion;
	}

	@Transactional
	@Override
	public Confirmacion cambiarEstadoEdicionCurso(EdicionCurso edicionCurso, EstadoEdicionCurso estadoEdicionCurso)
			throws Exception {
		Confirmacion confirmacion = new Confirmacion();
		
		if(estadoEdicionCurso == null){
			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			//TODO
			//confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_CURSO);
			return confirmacion;
		}

		//EdicionCurso edicionCursoBd = buscarEdicionCurso(edicionCurso);
		edicionCurso.setEstadoEdicionCurso(estadoEdicionCurso);
		
		repoEdicionCurso.saveUpdate(edicionCurso);
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		
		return confirmacion;
	}

	@Override
	public EdicionCurso buscarEdicionCurso(EdicionCurso edicionCurso)
			throws Exception {

		return (EdicionCurso)repoEdicionCurso.findById(edicionCurso.getId(), EdicionCurso.class);
	}

	@Transactional
	@Override
	public Confirmacion inscribirParticipante(EdicionCurso edicionCurso ,Usuario usuario, TipoParticipante tipoParticipante, EstadoParticipante estadoParticipante ) throws Exception{
		
		Confirmacion confirmacion = new Confirmacion();
		Date fechaHoy = new Date();
		Date fechaDesdeEdicion = edicionCurso.getFechaDesde(); 
		List<Participante> participantes ;
		Participante participante = new Participante();
		
		if( fechaDesdeEdicion== null){

			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_FECHA_DESDE_NULA);
			return confirmacion;

		}

		if(fechaHoy.after(fechaDesdeEdicion)){
			
			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_INSCRIPCION_CERRADA);
			return  confirmacion;
		}
		//TODO ESTO VA A CAMBIAR CUANDO SE DEFINA BIEN LOS TIPOS DE USUARIOS 
		//TENER EN CUENTA QUE VAN A VER USARIOS SIN MANAGER PARA MANDAR MAIL
		if(usuario.getManager() == null && usuario.getTipoUsuario() != TipoUsuario.PM){

			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_PM_NO_ASIGNADO);
			return  confirmacion;

		}
		if(edicionCurso.getParticipantes()== null){
			participantes = new ArrayList<Participante>();
		}else{
			participantes = edicionCurso.getParticipantes();
		}

		if(participantes!= null && this.isFound(participantes, usuario)){

			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			confirmacion.setErrorString(Constants.EDICIONCURSO_ERROR_PARTICIPANTE_YA_EXISTE);
			return confirmacion;

		}
		//TODO MANDAR MAIL AL PM DEL PARTICIPANTE
		participante.setEdicionCurso(edicionCurso);
		participante.setEstadoParticipante(estadoParticipante);
		participante.setTipoParticipante(tipoParticipante);
		participante.setUsuario(usuario);
		repoParticipante.saveUpdate(participante);
		
		SimpleMailMessage msg = new SimpleMailMessage();
		Map<Object, Object> hTemplateVariables = new HashMap<Object, Object>();
		hTemplateVariables.put(Constants.USUARIO_PM, participante.getUsuario().getManager());
		hTemplateVariables.put(Constants.USUARIO, participante.getUsuario());
		hTemplateVariables.put(Constants.EDICIONCURSO, participante.getEdicionCurso());
		msg.setSubject("Permiso Para Realizar Curso");
		sendSolicitudParaRealizarCurso.send(msg, hTemplateVariables);
		
		participantes.add(participante);
		edicionCurso.setParticipantes(participantes);
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		confirmacion.setErrorString(Constants.EDICIONCURSO_OK_INSCRIBIR_PARTICIPANTE);
		return confirmacion;
	}

	private boolean validarParticipantes(List<Participante> participantes){
		int cantidadDocentes = 0;
		int cantidadRRHH = 0;

		for (Participante participante: participantes){
			if(participante.getTipoParticipante().equals(TipoParticipante.DOCENTE))
				cantidadDocentes++;
			if(participante.getTipoParticipante().equals(TipoParticipante.RRHH))
				cantidadRRHH++;
		}
		if(cantidadDocentes == 0 || cantidadRRHH == 0)
			return false;
		
		return true;
	}

	private boolean isFound(List<Participante> participantes, Usuario usuario){
		
		for (Participante unParticipante : participantes) {
			
			if(unParticipante.getUsuario().getId().compareTo(usuario.getId()) == 0){
				return true;
			}
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<EdicionCurso> listarEdicionesCursosPublicado() throws Exception {

		return repoEdicionCurso.getEdicionCursoPorEstado(EstadoEdicionCurso.PUBLICADO);

	}
	
	public List<EdicionCurso> listarEdicionesCursosAsistidos(Usuario usuario) throws Exception {

		return repoEdicionCurso.getEdicionCursoPorEstado(EstadoEdicionCurso.PUBLICADO);

	}

	@Override
	public List<EdicionCurso> listarEdicionesCursos() throws Exception {
		//logger.info("getEdicionList");
		return repoEdicionCurso.getListEntity(EdicionCurso.class);
		//return repoEdicionCurso.getEdicionCursoPorEstado(EstadoEdicionCurso.NO_PUBLICADO);
	}
	
	@Override
	public List<EdicionCurso>listarEdicionCursosPorUsuario(Usuario usuario) throws Exception{
		List<Participante>listaParticipantes = repoParticipante.listarParticipantePorUsuario(usuario, EstadoParticipante.PRESENTE);
		List<EdicionCurso>listaEdicionCursos = new ArrayList<EdicionCurso>();
		for(Participante participante:listaParticipantes){
			listaEdicionCursos.add(participante.getEdicionCurso());
		}
		return listaEdicionCursos;
	}

	@Override
	public List<PresentismoSesionEdicionCurso> buscarPresentismoEdicion(EdicionCurso edicionCurso) throws Exception {
		return repoEdicionCurso.buscarPresentismoEdicion(edicionCurso);
	}

	@Override
	public Confirmacion guardarPresentismo(List<PresentismoSesionEdicionCurso> presentismosSesionEdicionCurso) throws Exception {
		Confirmacion confirmacion = new Confirmacion();
		
		//mandar mail para feedback
		//TODO:AL participante y ponerlo en presente, siepre y cuando este presente en alguna sesion
		//Ahora pone Presente si al menos estuvo una vez en la sesion, pero en ningun lado se pone ausente, si no presencio nungna sesion
		if(presentismosSesionEdicionCurso.size() > 0){
			EdicionCurso edicionCurso = new EdicionCurso();
			edicionCurso = presentismosSesionEdicionCurso.get(0).getSesionEdicionCurso().getEdicionCurso();
			edicionCurso.setEstadoEdicionCurso(EstadoEdicionCurso.DICTADO);
			repoEdicionCurso.saveUpdate(edicionCurso);
		}
//		int cantSesiones = presentismosSesionEdicionCurso.get(0).getSesionEdicionCurso().getEdicionCurso().getSesionCurso().size();
		for (PresentismoSesionEdicionCurso unPresentismoSesionEdicionCurso:presentismosSesionEdicionCurso) {
			boolean presente=false; //Si estuvo al menos presente en una sesion, se le pone estado presente para feedback
			Participante participante = unPresentismoSesionEdicionCurso.getParticipante();
			
			if(unPresentismoSesionEdicionCurso.getEstadoPresentismo().compareTo(EstadoPresentismoSesionEdicion.PRESENTE) == 0){
				participante.setEstadoParticipante(EstadoParticipante.PRESENTE);
				repoParticipante.saveUpdate(participante);
			}
			
			//repoStatic guarda cualquier objecto
			repoStatic.saveUpdate(unPresentismoSesionEdicionCurso);
		}
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		return confirmacion;
		
	}
	
	@Override
	public List<EdicionCurso> buscarEdicionCursosAprobados(Usuario usuario) throws Exception {
		return repoEdicionCurso.getEdicionCursosAprobados(usuario);
	}
	
	@Override
	public List<EdicionCurso> buscarEdicionesCursosDictadas() throws Exception {
		return repoEdicionCurso.buscarEdicionesCursosDictadas();
	}
}
